package api;
import common.CodeInValidException;

import com.fasterxml.jackson.databind.ObjectMapper;
import compile.Answer;
import compile.Question;
import compile.Task;
import dao.Problem;
import dao.ProblemDao;

import common.CodeInValidException;
import common.ProblemNotFoundException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

@WebServlet("/result")
public class ResultServlet extends HttpServlet {
    /**
     * 结果的 请求body格式
     */
    static class ResultRequest{
        public int id;
        public String code;
    }

    /**
     * 结果的 响应body格式
     */
    static class ResultResponse{
        //error 0: 编译运行成功 1: 编译失败 2: 运行错误(异常错误) 3: 其他错误
        public int error;
        public String reason;
        public String stdout;
    }
    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("用户当前的工作目录: " + System.getProperty("user.dir"));
        ResultRequest resultRequest = new ResultRequest();
        ResultResponse resultResponse = new ResultResponse();
        try{
            resp.setStatus(200);
            resp.setContentType("application/json;charset=utf8");
            // 1. 读取请求正文, 按照json格式读取
            String body = readBody(req);
            resultRequest = objectMapper.readValue(body, ResultRequest.class);
            // 2. 根据 id 从数据库中查找到题目的测试用例
            ProblemDao problemDao = new ProblemDao();
            Problem problem = problemDao.selectOne(resultRequest.id);
            if (problem == null) {
                // 这里就是不存在 该id的题目
                throw new ProblemNotFoundException();
            }
            // 测试用例代码
            String testCode = problem.getTestCode();
            // 提交的代码
            String requestCode = resultRequest.code;
            // 3. 把用户提交的代码和测试用例的代码,给瓶装成一个完整的代码
            String finalCode = mergeCode(requestCode,testCode);
            if (finalCode == null){
                // 这里就是最终提交的代码为空的情况
                throw new CodeInValidException();
            }
            // 4. 创建一个 Task 实例, 调用里面的 compileAndRun 来进行编译运行
            Task task = new Task();
            Question question = new Question();
            question.setCode(finalCode);
            Answer answer = task.compileAndRun(question);
            // 5. 根据 Task 运行的结果, 包装成一个 Http 响应
            resultResponse.error = answer.getError();
            resultResponse.reason = answer.getReason();
            resultResponse.stdout = answer.getStdout();
        } catch (ProblemNotFoundException e) {
            resultResponse.error = 3;
            resultResponse.reason = "当前不存在这个id: " + resultRequest.id;
        } catch (CodeInValidException e) {
            resultResponse.error = 3;
            resultResponse.reason = "提交的代码不符合要求!";
        } finally {
            // 这两行代码 不论是否异常都会执行, 所以可以放到finally中执行
            String respString = objectMapper.writeValueAsString(resultResponse);
            resp.getWriter().write(respString);
        }

    }

    /**
     * 测试用例代码和提交的代码合并
     * @param requestCode 提交的代码
     * @param testCode 测试用例代码
     * @return 返回合并后的代码
     */
    private String mergeCode(String requestCode, String testCode) {
        // 找到最后一个"}"的位置
        int index =requestCode.lastIndexOf("}");
        if (index == -1){
            // 这里是不存在的情况
            return null;
        }
        // 截取前面的代码
        String curCode = requestCode.substring(0,index);
        // 拼接代码
        return curCode + testCode + "\n}";
    }


    /**
     * 读取请求的正文
     * @param req 请求
     * @return 以字符串形式返回
     * @throws UnsupportedEncodingException 字符编码不支持的异常
     */
    private String readBody(HttpServletRequest req) throws UnsupportedEncodingException {
        // 1. 根据 Content-Length 获取 body 中的长度(单位是字节)
        int contentLength = req.getContentLength();
        // 2. 按照找个长度准备一个 byte[]
        byte[] buffer = new byte[contentLength];
        // 3. 通过 req 里面的 getInputStream 方法, 获取body的流对象(body比较长)
        try (InputStream inputStream = req.getInputStream()){
            // 4. 基于这个流对象,读取内容,将读取到内容放到byte[]数组中
            inputStream.read(buffer);
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 5. 把这个 byte[] 的内容构造成一个 String
        return new String(buffer,"utf8");
    }
}
